import { fwFaces } from './attributes.js'

export const geoTypes = ["@YS-剪力墙", "@YS-人防墙", "@YS-梁"];
/**
 *
 * @param {string} type  构件类型
 * @param {Array<object>} cadpoints  坐标信息，包含x,y
 * @param {number} floorHeight 层高
 * @param {number | undefined | null} height 构件高 梁，板才有
 */

const pointsFrom4To8 = (type, cadpoints, floorHeight = 38000, height = 500) => {
  let iSize = 0;
  if (cadpoints instanceof Array) {
    iSize = cadpoints.length;
  }
  // if (iSize != 4) {
  //   throw new Error("暂时只支持处理矩形构件");
  // }
  if (!geoTypes.includes(type)) {
    throw new Error("暂时只支持梁、墙、板的处理");
  }

  // 先处理数据
  floorHeight = floorHeight / 10; // 层高对不上，先除以10
  let topfacePoints = [];
  cadpoints.forEach((e) => {
    typeof e.x != "number" && (e.x = Number.parseFloat(e.x));
    typeof e.y != "number" && (e.y = Number.parseFloat(e.y));

    // 这里数值太大，X统一减去2200000（220万）,Y统一减去200000
    e.x = Math.round(e.x) - 2200000;
    e.y = Math.round(e.y) + 200000; //负的

    if (type == "@YS-梁") {
      e.z = floorHeight - height;
    }
    topfacePoints.push({
      x: e.x,
      y: e.y,
      z: floorHeight,
    });
  });
  cadpoints.push(...topfacePoints);
  let vectors = [];
  vectors = cadpoints.map((e) => [e.x, e.y, e.z]);
  // console.warn(vectors);
  return vectors;
};

// 计算索引
const calcIndices = (vectors) => {
  // 记录z轴的数值
  const z0 = vectors[0][2]
  const z1 = vectors[vectors.length - 1][2]
  let indices = [];
  let length = vectors.length;
  let basePoints = vectors.slice(0, length / 2)
  if (isClockwise(basePoints)) {  // 先将点设置为逆时针顺序
    basePoints.reverse()
  }
  // 第一个角度大于180度的情况下，耳切算法会进入死循环，需要特殊处理
  setPointsIndex(basePoints)
  //  点顺序改变以后，传给three.js的点的顺序也需要更新
  let newvectors = []
  newvectors.push(...basePoints)
  let topPoints = basePoints.map(e => [e[0], e[1], z1])
  newvectors.push(...topPoints)
  let bottomPoints = basePoints.map((e, index) => ({ x: e[0], y: e[1], id: index }));
  //底部
  let bottomIndices = triangulate(bottomPoints)
  indices.push(...bottomIndices)
  // 顶部
  let topIndices = bottomIndices.map(e => e + length / 2)
  indices.push(...topIndices)

  //侧面
  for (let index = 0; index < length / 2; index++) {
    if (index == length / 2 - 1) {
      indices.push(index);
      indices.push(0);
      indices.push(length / 2);

      indices.push(index);
      indices.push(length / 2);
      indices.push(index + length / 2);
    } else {
      indices.push(index);
      indices.push(index + 1);
      indices.push(index + length / 2 + 1);

      indices.push(index);
      indices.push(index + length / 2 + 1);
      indices.push(index + length / 2);
    }
  }

  return { newvectors, idx: indices };
};

// 计算模板的关键属性
const calcFWvers = (size, x, y, h = 100) => {
  const width = size.w, height = size.h
  const halfw = width / 2, halfh = height / 2
  const vertices = [
    [x - halfw, y - halfh, 0],
    [x + halfw, y - halfh, 0],
    [x + halfw, y + halfh, 0],
    [x - halfw, y + halfh, 0],
    [x - halfw, y - halfh, h],
    [x + halfw, y - halfh, h],
    [x + halfw, y + halfh, h],
    [x - halfw, y + halfh, h],
  ]
 
  // 生成顶点数组
  const facesVertices = [];
  fwFaces.forEach(face => {
    face.forEach(index => {
      facesVertices.push(...vertices[index]);
    });
  });

  return facesVertices;
}

// 计算6个面的中心点位置
export const calcFaceCenter = (faceArr) => {
  let points = faceArr.map(e => {
    let x = (e[0][0] + e[2][0]) / 2
    let y = (e[0][1] + e[2][1]) / 2
    let z = (e[0][2] + e[2][2]) / 2
    return [x,y,z]
  })
  return points
}

// 计算新的点
export const calcNewPositions = (face,vectors,distance) => {
  let changeIndex = null;
  if(face == 0 || face == 1){ //0 1 Z
    changeIndex = 2
  }else if (face == 2 || face == 3){  // Y 
    changeIndex = 1
  }else { // 4 5 X
    changeIndex = 0
  }

  let pointIndices = fwFaces[face]
  let relatedIndex = []
  fwFaces.forEach((e ,index) => {
    e.forEach((elm,iindex) => {
      if(pointIndices.includes(elm)){
        relatedIndex.push([index,iindex])
      }
    })
  })
  relatedIndex.forEach(e => {
    vectors[e[0]][e[1]][changeIndex] += distance
  })
  return vectors
}

// 判断点的顺序是顺时针还是逆时针 false (逆时针) true (顺时针) 
function isClockwise(points) {
  let sum = 0;

  for (let i = 0; i < points.length; i++) {
    const [x1, y1] = points[i];
    const [x2, y2] = points[(i + 1) % points.length]; // 确保闭合多边形
    sum += (x2 - x1) * (y2 + y1);
  }

  return sum > 0; // 如果面积为正，表示逆时针；负值表示顺时针
}

function isConcavePoint(p1, p2, p3) {
  // 计算叉积
  const crossProduct = (p2[0] - p1[0]) * (p3[1] - p2[1]) - (p2[1] - p1[1]) * (p3[0] - p2[0]);
  return crossProduct < 0; // 如果叉积为负，说明是凹点
}

// 第一个角是否是钝角
function isobtuse(points) {
  const n = points.length;
  if (n < 3) return false; // 少于三个点不能构成多边形


  for (let i = 0; i < n; i++) {
    const p1 = points[i];
    const p2 = points[(i + 1) % n];
    const p3 = points[(i + 2) % n];

    if (isConcavePoint(p1, p2, p3)) {
      if (i == 0) {
        return true
      }
    }
  }
  return false
}

function setPointsIndex(points) {
  if (isobtuse(points)) {
    points.splice(0, 0, points[points.length - 1])
    points.splice(points.length - 1, 1)
    setPointsIndex(points)
  }
}

/**
 * 耳切法（Ear Clipping Algorithm）
 */

function distanceSq(a, b) {
  return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}

function angle(a, b, c) {
  var temp =
    Math.atan2(c.y - b.y, c.x - b.x) - Math.atan2(a.y - b.y, a.x - b.x);
  return temp < 0 ? Math.PI * 2 + temp : temp;
}

function Triangle(a, b, c) {
  this.a = a;
  this.b = b;
  this.c = c;
  this.area = function () {
    return (a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2;
  };
}

function measureAngles(points) {
  for (var i = 0; i < points.length + 1; i++) {
    points[(i + 1) % points.length].angle =
      (angle(
        points[i % points.length],
        points[(i + 1) % points.length],
        points[(i + 2) % points.length]
      ) *
        180) /
      Math.PI;
  }
}

function isInsideTriangle(triangle, point) {
  var areaA, areaB, areaC;
  areaA = new Triangle(triangle.a, triangle.b, point).area();
  areaB = new Triangle(triangle.b, triangle.c, point).area();
  areaC = new Triangle(triangle.c, triangle.a, point).area();
  return (
    Math.sign(areaA) == Math.sign(areaB) && Math.sign(areaA) == Math.sign(areaC)
  );
}

function triangulate(points) {
  let indices = []
  if (points < 3) return;

  if (angle(points[0], points[1], points[2]) > Math.PI) {
    points.reverse();
  }

  var i = 1;
  var a, b, c;

  measureAngles(points);
  while (points.length > 3) {
    a = points[i % points.length];
    b = points[(i + 1) % points.length];
    c = points[(i + 2) % points.length];

    if (angle(a, b, c) < Math.PI) {
      var tempTriangle = new Triangle(a, b, c);
      var isEar = true;
      for (var j = i + 3; j < points.length + i + 3; j++) {
        if (isInsideTriangle(tempTriangle, points[j % points.length]))
          isEar = false;
      }
      if (isEar) {
        points.splice((i + 1) % points.length, 1); // erase middle point

        indices.push(c.id);
        indices.push(b.id);
        indices.push(a.id);
      }
    }
    i++;
  }
  indices.push(points[2].id);
  indices.push(points[1].id);
  indices.push(points[0].id);

  return indices
}

export default {
  pointsFrom4To8,
  calcIndices,
  calcFWvers,
  calcFaceCenter,
  calcNewPositions
};
